--- 模块功能：时钟管理
-- @author zgt
-- @module create
-- @license MIT
-- @copyright zgt
-- @release 2022-1-6
module(..., package.seeall)
require "bit"
require "misc"

local i2cid = 2
-- 1010 000x
-- 7bit地址，不包含最后一位读写位 bit.rshift(x,1) 逻辑右移
local i2cslaveaddr = bit.rshift(0xA2, 1) -- 0x51
local REG_AIE = 0X01
local REG_SEC = 0x02
local REG_MIN = 0x03
local REG_HOUR = 0x04
local REG_DAY = 0x05
local REG_WEEK = 0x06
local REG_MON = 0x07
local REG_YEAR = 0x08
local REG_M_ALARM = 0x09

local log_prefix = "pcf8563->"
local alarmVal
-- 初始化并打开I2C操作
-- @param I2C 内部ID
-- @return number ,I2C的速率
local function i2c_open()
    local b = i2c.setup(i2cid, i2c.SLOW, i2cslaveaddr)
    if b ~= i2c.SLOW then
        i2c.close(i2cid)
        return i2c.setup(i2cid, i2c.SLOW,i2cslaveaddr)
    end
    return b
end

local function i2c_send(data)
    i2c.send(i2cid, i2cslaveaddr, data)
end
local function i2c_recv(reg, num)
    i2c.send(i2cid, i2cslaveaddr, reg)
    return i2c.recv(i2cid, i2cslaveaddr, num)
end
local function bcd_to_hex(data)
    -- local hex = ((data>>4)*10)+(data&0x0f)
    local hex = (bit.rshift(data, 4) * 10) + bit.band(data, 0x0f)
    return hex
end
local function hex_to_bcd(data)
    -- local hex = ((data/10)<<4)+(data%10)
    local bcd = bit.lshift(math.floor(data / 10), 4) + (data % 10)
    return bcd;
end
local function getNextAlarmM(sh, siv, cH, cMin)
    local nH = sh * 60
    local cM = cH * 60 + cMin
    while true do
        if nH > cM then break end
        nH = nH + siv
    end
    if (nH >= 24 * 60) then nH = sh * 60 end
    return math.floor(nH / 60), nH % 60
end

function read_time()
    local t = {}
    local data = i2c_recv(REG_SEC, 7)
    -- local data = i2c.read(i2cid, REG_SEC, 7) -- 37 01 01 05 01 00
    -- log.warn(log_prefix, "read time_data:" .. string.toHex(data, ' '))
    if not data or #data < 7 then
        log.error(log_prefix,'读取实时时钟失败')
        return
    end
    t.sec = bcd_to_hex(bit.band(data:byte(1), 0x7f))
    t.min = bcd_to_hex(bit.band(data:byte(2), 0x7f))
    t.hour = bcd_to_hex(bit.band(data:byte(3), 0x3F))
    t.day = bcd_to_hex(bit.band(data:byte(4), 0x3F))
    t.week = bit.band(data:byte(5), 0x07) + 1
    t.month = bcd_to_hex(bit.band(data:byte(6), 0x1F))
    t.year = bcd_to_hex(data:byte(7)) + 2000
    return t
end
--- 设定闹钟
---@param h 几点开始
---@param m 间隔时间？分钟
function setAlarm(h, m)
    if (not m or not h) then log.error('闹钟设置参数不全') return end
    -- 1、获取当前时间
    -- local t = os.date("*t")
    local t = read_time()
    if not t or not t.hour or not t.min then  log.error('读取外部时钟失败')  t = os.date("*t") end
    nextHH, nextMM = getNextAlarmM(h, m, t.hour, t.min)
    -- 清除闹钟标志并开启闹钟
    i2c_send({REG_AIE, 0x02})
    -- 设置闹钟时间 时&分
    i2c_send({REG_M_ALARM, hex_to_bcd(nextMM), hex_to_bcd(nextHH)})
    -- 判断是否设定成功
    local alarm = i2c_recv(REG_M_ALARM, 2)
    if not alarm or #alarm < 2 then  log.error('读取设定的闹钟时间失败')  return end
    alarmVal = string.format('%02d:%02d', bcd_to_hex(bit.band(alarm:byte(2), 0x3F)), bcd_to_hex(alarm:byte(1)))
    log.warn(log_prefix, '读取设定的闹钟时间:' .. alarmVal)
    if bcd_to_hex(alarm:byte(1)) == nextMM and bcd_to_hex(bit.band(alarm:byte(2), 0x3F)) == nextHH then
        --log.warn(log_prefix, string.format("设定闹钟成功,下次启动时间:%02d点%02d分", nextHH, nextMM))
        return true
    end
end
--- 读取闹钟,返回 mmhh
function readAlarm()
    return alarmVal or "NOSET"
end
--- 读取闹钟标志位
function readAlarmFlag()
    local aie = i2c_recv(REG_AIE, 1)
    if aie then
        local aieval = aie:byte(1)
        log.info(log_prefix, "读取闹钟标识:", aieval)
        if aieval then
            return bit.isset(aieval, 3)
        end
    else
        log.error(log_prefix, "读取闹钟标识失败")
        init()
    end
    return false
end

function set_time(time)
    time = time or misc.getClock()
    log.warn(log_prefix, "正在同步外部时钟:" .. json.encode(time))
    -- misc.getClock() {"hour":16,"min":3,"wday":3,"day":11,"month":1,"year":2022,"sec":46,"yday":11,"isdst":false}
    time.week = time.wday or misc.getWeek()
    -- set time
    if (time.year > 2000) then time.year = time.year - 2000 end
    local data7 = hex_to_bcd(time.year)
    local data6 = hex_to_bcd(time.month)
    local data5 = time.week - 1
    local data4 = hex_to_bcd(time.day)
    local data3 = hex_to_bcd(time.hour)
    local data2 = hex_to_bcd(time.min)
    local data1 = hex_to_bcd(time.sec)
    i2c_send({REG_SEC, data1, data2, data3, data4, data5, data6, data7})
    sys.publish("PCF_SETTIME")
end
function init()
    local iic = i2c_open()
    if iic and iic ~= 0 then
        log.warn(log_prefix, "iic open is ok! ", iic)
    else
        log.error("iic 打开失败！")
    end
end
init()
--- 防止联网失败，时间错误
local pcf8563_time = read_time()
if(pcf8563_time and pcf8563_time.year > os.date("*t").year) then
    misc.setClock(pcf8563_time) 
end
sys.subscribe("NTP_SUCCEED", set_time)
sys.subscribe("DTU_SETTIME", set_time)

sys.subscribe("PCF_SETTIME", function( )
    local day = i2c_recv(0x09, 4)
    log.warn("TEST",day:toHex())
end) 